/**
 * Drag&Drop support
 * 
 * Override E.DragSource.createDrag() to create a specialized E.DragView, acting on the
 * different events, fired in the drag, drop process.
 * 
 * Use E.DragMoveSource to simply reposition an element.
 */

/**
 * @class Source for a drag&drop process.
 * @param {E.Dom} dom The extended DOM element to drag around.
 * @param {element} sensor The DOM element, sensible to the mouse down, starting the drag
 *                         process.
 * @constructor
 */
E.DragSource = function(dom, sensor) {
  this.box = dom;
/*  if (!this.box.element.style.left)
    this.box.element.style.left = 0;
  if (!this.box.element.style.top)
    this.box.element.style.top = 0;*/
  this.offsetV = [0,0];
  try {
  if (this.box.element.offsetParent)
    this.offsetV = Dom(this.box.element.offsetParent).getVector();
  }
  catch (ex) {
  	// IE 7 can run into a 'Unspecified Error' exception...
  }
  var ds = this;
  new EventListener(sensor, "mousedown", function(evn) {
    var anchorV = [evn.clientX, evn.clientY];
    ds.createDrag(anchorV);
    return EventListener.cancelEvent(evn);
  });
}

E.DragSource.prototype.createDrag = function(anchorV) {
  return new E.DragView(this, anchorV);
}

E.DragSource.prototype.createDragViewDom = function() {
	var s = this.box.element;
	var d = this.box.cloneNode(true);
	d.style.position = "absolute";
	d.style.width = s.offsetWidth + "px";
  d.style.zIndex = 4;
	s.parentNode.appendChild(d);
  return Dom(d);
}

E.DragSource.prototype.createDragDraftDom = function() {
	var s = this.box.element;
	var d = this.box.cloneNode(true);
	s.parentNode.appendChild(d);
  return Dom(d);
}


E.DragView = function(dragSource, anchorV) {
	this.source = dragSource;
	this.anchorV = anchorV;
  this.startV = this.source.box.getVector();
  this.dropping = false;
  this.dragging = false;
  this.targets = null;
  var dv = this;
  this.evMouseUp = new EventListener(document, "mouseup", function(evn) {
    if (dv.dragging) {
	    dv.drop(evn);
	    return EventListener.cancelEvent(evn);
    }
    else {
    	dv.cleanup();
    }
  });
  this.evMouseMove = new EventListener(document, "mousemove", function(evn) {
    dv.drag(evn);
    return EventListener.cancelEvent(evn);
  });
  this.evKeyDown = new EventListener(document, "keydown", function(evn) {
    dv.onKeyDown(evn);
  });
  this.evKeyUp = new EventListener(document, "keyup", function(evn) {
    dv.onKeyUp(evn);
  });
}

E.DragView.prototype.start = function(evn) {
  this.dragging = true;
  this.onDragStart(evn);
  this.dom.moveTo(this.startV);
}

E.DragView.prototype.onDragStart = function(evn) {
	this.dom = this.source.createDragViewDom();
	this.draft = this.source.createDragDraftDom();
	if (this.draft)
	  this.draft.hide();
}

E.DragView.prototype.drag = function(evn) {
	if (this.dropping)
	  return;
  var v = [evn.clientX - this.anchorV[0], evn.clientY - this.anchorV[1]];
  if ((v[0] == 0) && (v[1] == 0))
    return;
  if (null == this.dom) {
  	if (!this.startTimer) {
  		var self = this;
  	  this.startTimer = window.setTimeout(function() { self.start(evn); }, 200);
  	}
  	return;
  }
  if (this.onDrag && !this.onDrag(v, evn))
    return;
//  this.dom.element.innerHTML = "x: "+v[0]+" y: "+v[1]+" | x: "+
//    this.anchorV[0]+" y: "+this.anchorV[1]+" | x: "+
//    this.source.offsetV[0]+" y: "+this.source.offsetV[1]+" | x: "+
//    this.startV[0]+" y: "+this.startV[1];
	this.dom.moveTo([this.source.offsetV[0] + this.startV[0] + v[0], this.source.offsetV[1] + this.startV[1] + v[1]]);
  if (this.dragState)
    this.dragState.moveTo([evn.clientX, evn.clientY - 20]);
  var e = { dragView: this, dropTargetLevel: 0 };
  for (var m in evn)
    e[m] = evn[m];
  e.type = "dragOver";
  var targets = Dom(document.body).getElementsByVector([evn.clientX, evn.clientY]);
  for (var i = 0; i < targets.length; ++i) {
  	var t = targets[i];
  	if (this.dom.isAncestorOf(t))
  	  continue;
    e.dropTargetLevel++;
  	Dom(t).dispatchEvent(e);
		t.onDragOver && t.onDragOver(e);
  }
  if (this.targets) {
  	e.type = "dragOut";
    e.dropTargetLevel = 0;
	  for (var i = 0; i < this.targets.length; ++i) {
	  	var t = this.targets[i];
	  	if (this.dom.isAncestorOf(t))
	  	  continue;
	  	if (targets.indexOf(t) < 0) {
			  e.dropTargetLevel++;
        Dom(t).dispatchEvent(e);
	  	  t.onDragOut && t.onDragOut(e);
	  	}
	  }
  }
  this.targets = targets;
}

E.DragView.prototype.onKeyDown = function(evn) {
  if (evn.keyCode == 27) // ESC
  	this.cancel(evn);
}

E.DragView.prototype.drop = function(evn) {
  var v = [evn.clientX - this.anchorV[0], evn.clientY - this.anchorV[1]];
  if (this.onDrop && !this.onDrop(v, evn))
    return;
	this.dropping = true;
  var e = { dragView: this, dropTargetLevel: 0 };
  for (var m in evn)
    e[m] = evn[m];
  e.type = "dropInto";
  var targets = Dom(document.body).getElementsByVector([evn.clientX, evn.clientY]);
  for (var i = 0; i < targets.length; ++i) {
  	var t = targets[i];
  	if (this.dom && this.dom.isAncestorOf(t))
  	  continue;
	  e.dropTargetLevel++;
    Dom(t).dispatchEvent(e);
		t.onDropInto && t.onDropInto(e);
  }
  this.cleanup();
}

E.DragView.prototype.cancel = function(evn) {
  if (this.onCancel && this.onCancel(evn))
    this.cleanup();
}

E.DragView.prototype.cleanup = function() {
  this.evMouseUp.remove();
  this.evMouseMove.remove();
  this.evKeyDown.remove();
  this.evKeyUp.remove();
  this.dragging = false;
  if (this.dom) {
	  var d = this.dom.element;
	  if (d.parentNode)
	    d.parentNode.removeChild(d);
	  this.dom = null;
  }
  if (this.draft) {
	  var d = this.draft.element;
	  if (d.parentNode)
	    d.parentNode.removeChild(d);
	  this.draft = null;
  }
  if (this.startTimer) {
  	window.clearTimeout(this.startTimer);
    this.startTimer = null;
  }
}

E.DragView.prototype.repositionAnchor = function(vector) {
	this.anchorV = [this.anchorV[0] + vector[0], this.anchorV[1] + vector[1]];
}


E.DragMoveSource = function(dom, sensor) {
  E.DragMoveSource._super.call(this, dom, sensor);
}
Object.extend("E.DragMoveSource", "E.DragSource");

E.DragMoveSource.prototype.createDrag = function(anchorV) {
  return new E.DragMoveView(this, anchorV);
}

E.DragMoveSource.prototype.createDragViewDom = function() {
  return this.box;
}

E.DragMoveSource.prototype.createDragDraftDom = function() {
  return null;
}


E.DragMoveView = function(dragSource, anchorV) {
  E.DragMoveView._super.call(this, dragSource, anchorV);
}
Object.extend("E.DragMoveView", "E.DragView");

E.DragMoveView.prototype.onDrop = function(v, evn) {
	this.dom = null;
  return true;
}

